package com.lz.library.base

import android.annotation.SuppressLint
import android.app.Application
import android.content.Context
import android.content.res.Configuration
import android.os.Process
import androidx.collection.ArraySet
import com.lz.library.BuildConfig
import com.lz.library.net.NetworkManager
import com.lz.library.utils.FileUtils
import java.io.File
import java.io.PrintWriter
import java.io.StringWriter
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.ArrayList
import kotlin.system.exitProcess

/**
 *  author: you : 2020/10/28
 */
abstract class BaseApp : Application() {

    private val appInitModuleClasses: ArraySet<Class<out ApplicationInitModule>> = ArraySet()

    private val appInitModules: ArrayList<ApplicationInitModule> = ArrayList()

    override fun onCreate() {
        super.onCreate()
        context = this
        NetworkManager.register(this)
        Thread.setDefaultUncaughtExceptionHandler(CrashExceptionHandler())
        initApp()
        for (init in appInitModules) {
            init.onCreate(this)
        }
    }

    protected fun registerAppInit(className: String) {
        try {
            val clazz = Class.forName(className)
            if (ApplicationInitModule::class.java.isAssignableFrom(clazz) && !appInitModuleClasses.contains(clazz)) {
                appInitModuleClasses.add(clazz as Class<out ApplicationInitModule>)
                val module = clazz.newInstance() as ApplicationInitModule
                appInitModules.add(module)
            }
        } catch (_: ClassNotFoundException) {
        }
    }

    /**
     * 初始化
     */
    protected abstract fun initApp()

    override fun onTrimMemory(level: Int) {
        super.onTrimMemory(level)
        for (module in appInitModules) {
            module.onTrimMemory(level, this)
        }
    }

    override fun onLowMemory() {
        super.onLowMemory()
        for (module in appInitModules) {
            module.onLowMemory(this)
        }
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        for (module in appInitModules) {
            module.onConfigurationChanged(newConfig, this)
        }
    }

    private class CrashExceptionHandler : Thread.UncaughtExceptionHandler {

        private val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()

        override fun uncaughtException(t: Thread, e: Throwable) {
            saveCrashInfoToFile(e, CRASH_REPORTER, MAX_SIZE)
            defaultHandler?.uncaughtException(t, e)
        }

        fun saveCrashInfoToFile(ex: Throwable, fileName: String, maxSize: Long) {
            val sw = StringWriter()
            PrintWriter(sw).use {
                ex.printStackTrace(it)
                var cause = ex.cause
                while (cause != null) {
                    cause.printStackTrace(it)
                    cause = cause.cause
                }
            }
            val sb = StringBuilder(SimpleDateFormat("yyyy_MM_dd HH:mm:ss", Locale.getDefault()).format(Date()))
                .append("\n").append(sw.toString()).append("\n\n")
            val saveFile = File(FileUtils.getCacheDir(), fileName)
            val append = !saveFile.exists() || saveFile.length() <= maxSize
            FileUtils.writeFile(sb.toString(), saveFile, append)
            if (BuildConfig.isRelease) {
                Process.killProcess(Process.myPid())
                exitProcess(10)
            }
        }
    }

    companion object {
        /**
         * 日志文件上限大小
         */
        private const val MAX_SIZE = 1024 * 1024L

        /**
         * 日志文件名
         */
        const val CRASH_REPORTER = "crash-file.txt"

        @SuppressLint("StaticFieldLeak")
        internal lateinit var context: Context
    }

}